﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries.Utilities;
using NetTopologySuite.Operation.Overlay;

namespace NetTopologySuite.Operation.Union
{
	public class UnaryUnionOp
	{
		public static IGeometry Union(IList<IGeometry> geoms)
		{
			return new UnaryUnionOp(geoms).Union();
		}

		public static IGeometry Union(IList<IGeometry> geoms, IGeometryFactory geomFact)
		{
			return new UnaryUnionOp(geoms, geomFact).Union();
		}

		public static IGeometry Union(IGeometry geom)
		{
			return new UnaryUnionOp(geom).Union();
		}

		public UnaryUnionOp(IEnumerable<IGeometry> geoms)
		{
			this.Extract(geoms);
		}

		public UnaryUnionOp(IEnumerable<IGeometry> geoms, IGeometryFactory geomFact)
		{
			this._geomFact = geomFact;
			this.Extract(geoms);
		}

		public UnaryUnionOp(IGeometry geom)
		{
			this.Extract(geom);
		}

		private void Extract(IEnumerable<IGeometry> geoms)
		{
			foreach (IGeometry geom in geoms)
			{
				this.Extract(geom);
			}
		}

		private void Extract(IGeometry geom)
		{
			if (this._geomFact == null)
			{
				this._geomFact = geom.Factory;
			}
			GeometryExtracter.Extract<IPolygon>(geom, this._polygons);
			GeometryExtracter.Extract<ILineString>(geom, this._lines);
			GeometryExtracter.Extract<IPoint>(geom, this._points);
		}

		public IGeometry Union()
		{
			if (this._geomFact == null)
			{
				return null;
			}
			IGeometry geometry = null;
			if (this._points.Count > 0)
			{
				IGeometry g = this._geomFact.BuildGeometry(this._points);
				geometry = this.UnionNoOpt(g);
			}
			IGeometry g2 = null;
			if (this._lines.Count > 0)
			{
				IGeometry g3 = this._geomFact.BuildGeometry(this._lines);
				g2 = this.UnionNoOpt(g3);
			}
			IGeometry g4 = null;
			if (this._polygons.Count > 0)
			{
				g4 = CascadedPolygonUnion.Union(this._polygons);
			}
			IGeometry geometry2 = UnaryUnionOp.UnionWithNull(g2, g4);
			IGeometry geometry3;
			if (geometry == null)
			{
				geometry3 = geometry2;
			}
			else if (geometry2 == null)
			{
				geometry3 = geometry;
			}
			else
			{
				geometry3 = PointGeometryUnion.Union((IPuntal)geometry, geometry2);
			}
			if (geometry3 == null)
			{
				return this._geomFact.CreateGeometryCollection(null);
			}
			return geometry3;
		}

		private static IGeometry UnionWithNull(IGeometry g0, IGeometry g1)
		{
			if (g0 == null && g1 == null)
			{
				return null;
			}
			if (g1 == null)
			{
				return g0;
			}
			if (g0 == null)
			{
				return g1;
			}
			return g0.Union(g1);
		}

		private IGeometry UnionNoOpt(IGeometry g0)
		{
            //IGeometry geom = this._geomFact.CreatePoint(null);

            IGeometry geom = null;
            return OverlayOp.Overlay(g0, geom, SpatialFunction.Union);
		}

		private readonly List<IGeometry> _polygons = new List<IGeometry>();

		private readonly List<IGeometry> _lines = new List<IGeometry>();

		private readonly List<IGeometry> _points = new List<IGeometry>();

		private IGeometryFactory _geomFact;
	}
}
